﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace DbView
{
	class DataTableHelpers
	{
		/* as you always forget
			dt.Rows.Add(new object[] { 1, "<clear filter>", "" });
		*/
		public static DataTable MakeDataTable(String name, String columnNames, String columnTypes)
		{
			String [] colNames = columnNames.Split('|');
			if (colNames.GetLength(0) != columnTypes.Length)
			{
				int n1 = colNames.GetLength(0);
				int n2 = columnTypes.Length;
				String msg = String.Format("Invalid table specification for MakeDataTable\n"+
										   "The number of columns must equal the number of column type characters.\n"+
										   "There are {0} names in {1}\n"+
										   "There are {2} chars in {3}", n1, columnNames,n2, columnTypes);
				throw new Exception (msg);
			}
			DataTable dt = new DataTable(name);
			for (int idx = 0; idx < colNames.GetLength(0); ++idx)
			{
				String colName = colNames[idx];
				String typeName = DataTableHelpers.resolveType(columnTypes[idx]);

				DataColumn col = new DataColumn();
				col.ColumnName = colName;
				col.DataType = System.Type.GetType(typeName);
				dt.Columns.Add(col);
			}
			return dt;
		}

		//
		// populate a row from input applying the given mapping
		// 
		internal static void AddRow(DataTable dt, String[] cells, int [] mapping)
		{
			int cellCount = cells.GetLength(0);
			int dataCount = dt.Columns.Count;
			int mappingCount = mapping.GetLength(0);
			if (cellCount > 0)
			{
				object [] data = new object [dataCount];
				for (int idx = 0; idx < cellCount; ++idx)
				{
					if (idx < mappingCount && mapping[idx] != -1)
					{
						String value = cells[idx];
						int dataIdx = mapping[idx];
						if (value.Length == 0)
							data[dataIdx] = System.DBNull.Value;
						else
						{
							Type t = dt.Columns[dataIdx].DataType;

							if (t == typeof(DateTime))
							{
								DateTime dat;
								bool parseOk = (DateTime.TryParse(value, out dat));
								if (parseOk)
									data[dataIdx] = dat;
								else
									data[dataIdx] = System.DBNull.Value;
							}
							else if(t == typeof(int))
							{
								int iVal;
								bool parseOk = (int.TryParse(value, out iVal));
								if (parseOk)
									data[dataIdx] = iVal;
								else
									data[dataIdx] = System.DBNull.Value;
									
							}
							else if (t == typeof(decimal))
							{
								decimal dVal;
								bool parseOk = (decimal.TryParse(value, out dVal));
								if (parseOk)
									data[dataIdx] = dVal;
								else
									data[dataIdx] = System.DBNull.Value;
							}
							else if (t == typeof(double))
							{
								double dVal;
								bool parseOk = (double.TryParse(value, out dVal));
								if (parseOk)
									data[dataIdx] = dVal;
								else
									data[dataIdx] = System.DBNull.Value;
									
							}
							else if(t == typeof(String))
							{
								data[dataIdx] = value;
							}
							else
							{
								Console.WriteLine("??? {0} ",t);
								data[dataIdx] = System.DBNull.Value;
							}
						}
					}
				}
				// fill in blanks.
				for (int idx = 0; idx < dataCount; ++idx)
					if (data[idx] == null)
						data[idx] =  System.DBNull.Value;

				dt.Rows.Add(data);
			}
		}

		public static DataTable MakeSortedTable(DataTable source, String field, bool asc)
		{
			DataTable target = source.Clone(); // copy structure so rows can be received
			String sortClause = String.Format("{0} {1}", field, (!asc) ? "DESC" : "");
			DataRow [] sorted = source.Select("", sortClause);
			foreach (DataRow sortedRow in sorted)
				target.ImportRow(sortedRow);
			return target;
		}
		
		private static String resolveType(char c)
		{
			switch (c)
			{
				case 'S': return "System.String";
				case 'I': return "System.Int32";
                case 'J': return "System.Int64";  // j = I++
                case 'N': return "System.Double";
				case 'D': return "System.DateTime";
				default:
					throw new Exception(String.Format("Invalid Type code {0} - expecting S,I,J, N,D", c));
			}
		}



		internal static bool HasMatchingColumns(DataTable table, DataTable previous)
		{
			if (table.Columns.Count != previous.Columns.Count) return false; // no
			for (int columnIndex = 0; columnIndex < table.Columns.Count; ++columnIndex)
			{
				if (previous.Columns[columnIndex].ColumnName != table.Columns[columnIndex].ColumnName)
					return false;
			}
			return true;
		}

		/*
			This to get the rainbow colours to show generations of changes when refreshing 
		*/
		internal static void CompareData(DataTable table, DataTable previous)
		{
			// two managers. One to set new colours. The other to get previous colour information
			// for updating generations
			DataTableExtraInfoManager mgrNew = new DataTableExtraInfoManager(table);
			DataTableExtraInfoManager mgrPrev = new DataTableExtraInfoManager(previous);
			// first find the key columns for matching the old table rows to the new
			List<int> pkey = new List<int>();
			for (int columnIndex = 0; columnIndex < table.Columns.Count; ++columnIndex)
			{
				if (mgrPrev.IsPkey(previous.Columns[columnIndex]))
					pkey.Add(columnIndex);
			}
			if (pkey.Count == 0) pkey.Add(0);   // if no pkey defined assume column 0 only (works for tables, views etc)
			int [] pkeys = pkey.ToArray();

			// Generation colours. Gen 1 is change now, then gen 2,3,4,5 and reverts to normal
			String[] genColours = new String[] {"Red", "DarkOrange", "Green", "Teal", "MediumBlue"};
			int generationCount = genColours.GetLength(0);
			// were building a new list, but using an old list
			// hence a list<> and array
			List<List<int>> genRows = new List<List<int>>(); // new coloured rows
			List<int []> prevGenRows = new List<int []>();	 // previous coloured rows
			// initialise these two
			for (int idx = 0; idx < generationCount; ++idx)
			{
				genRows.Add(new List<int>()); // new is empty
				prevGenRows.Add(mgrPrev.GetColourRows(genColours[idx])); // fetch previous
			}

			// foreach row in the new table
			int rowIndex = 0;
			foreach (DataRow dr in table.Rows)
			{
				// find row in previous
				bool found = false;
				int rowIndexp = 0;
				foreach (DataRow drp in previous.Rows)
				{
					if (IsRowMatch(dr, drp, pkeys)) // match on pkey data 
					{
						found = true;
						// if match then compare all data and set row colour to generation 0.
						if (CompareRowData(dr, drp) == false) // false implies something was updated
							genRows[0].Add(rowIndex);
						else
						{
							// The row is the same. If it previously was different move that information
							// down the generations
							// check generation of previous and move to next
							int newGeneration = CheckUpdateGeneration(rowIndexp, prevGenRows);
							if (newGeneration != -1) // -1 is n/a
								genRows[newGeneration].Add(rowIndex);
						}
						break;
					}
					++rowIndexp;
				}
				if (!found) // ie. utterly new row
					genRows[0].Add(rowIndex);
				++rowIndex;
			}
			// all the row colour data is ready to be applied to the new table now view the extra info manager.
			for (int idx = 0; idx < generationCount; ++idx)
				mgrNew.SetRowColour(genColours[idx], genRows[idx].ToArray());
		}

		private static int CheckUpdateGeneration(int tgtRowIndex, List<int[]> prevGenRows)
		{
			// don't scan the last generation
			for (int idx = 0; idx < prevGenRows.Count - 1; ++idx)
			{
				if (Array.IndexOf(prevGenRows[idx], tgtRowIndex) != -1)
					return idx + 1;
				//foreach (int rowIndex in prevGenRows[idx])
				//    if (rowIndex == tgtRowIndex)
				//        return idx + 1;
			}
			return -1; // not applicable
		}

		private static bool CompareRowData(DataRow dr, DataRow drp)
		{
			for (int cellIdx = 0; cellIdx < dr.ItemArray.GetLength(0); ++cellIdx)
			{
				if (!dr[cellIdx].Equals(drp[cellIdx]))
					return false;
			}
			return true;
		}

		private static bool IsRowMatch(DataRow dr, DataRow drp, int[] pkeys)
		{
			for (int idx = 0; idx < pkeys.GetLength(0); ++idx)
			{
				if (! dr[pkeys[idx]].Equals(drp[pkeys[idx]]))
					return false;
			}
			return true;
		}
	}
}
